iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0
Web 3

從以太坊白皮書理解 web 3 概念系列 第 23

從以太坊白皮書理解 web 3 概念 - Day22

  • 分享至 

  • xImage
  •  

從以太坊白皮書理解 web 3 概念 - Day22

Learn Solidity - Day 14 - Advanced zkSync Concept

今天將會透過 Lession 18 - Advanced zkSync Concept 來了解

透過 zkSync 如何做到 ERC20 token 交換。

ERC20 交換

假設要透過 zkSync 做 ERC20 Token 做交換

這邊會以 USDT 作為範例

首先需要先取得 Rinkeby 鏈上的 USDT token

取得流程如下

  1. 直接瀏覽到 USDT Contract Address
  2. 按下 Connect to Web3 連結,然後選擇要使用的錢包
  3. 然後選擇 Write Cotnract 選項
  4. 需要 mint (鑄造) 至少 6 USDT,
    mint 需要兩個參數如下:
    1. _to: 代表要使佣 Rinkeby address
    2. _amount: 代表要鑄造的數量 這邊填入 6000000
  5. 按下 Write
  6. 接著會看到 Metamask 彈出。選擇 Confirm

首先比需要先設定好 src/alice.js 來操作 USDT

實作 alice.js

  1. 改變 token 值為 'USDT'
  2. 改變 amoutToDeposit 值為 '6.0'
  3. 改變 amoutToTransfer 與 amountToWithdraw 值為 '2.0'

更新如下

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'USDT' // Update this line
  const amountToDeposit = '6.0' // Update this line
  const amountToTransfer = '2.0' // Update this line
  const amountToWithdraw = '2.0' // Update this line

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider)
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)
  const aliceInitialRinkebyBalance = await aliceRinkebyWallet.getBalance()
  console.log(`Alice's initial balance on Rinkeby is: ${ethers.utils.formatEther(aliceInitialRinkebyBalance)}`)

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  console.log('Depositing')
  await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, ethers)
  await utils.displayZkSyncBalance(aliceZkSyncWallet, ethers)
  await utils.registerAccount(aliceZkSyncWallet)

  console.log('Transferring')
  const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee, token, zksync, ethers)

  console.log('Withdrawing')
  const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token, zksync, ethers)
})()

調整單位更新功能

之前使用 ethers.util.formatEther 來對 ETH 單位做處理

但這邊需要使用 TokenSet 來做這件事

TokenSet

zkSync 提供 TokenSet 這個類別來做單位轉換的功能

為了方便使用會使用以下語法把 zkSyncProvider.tokenSet 重新存入 const tokenSet

const tokenSet = zkSyncProvider.tokenSet

可以使用以下語法把一般表示式轉為 BigNumber

const amountAsBigNumber = tokenSet.parseToken(token, amountInHumanReadableForm)

可以使用以下語法把 BigNumber 轉為一般單位

const amountInHumanReadableForm = tokenSet.formatToken(token, amountAsBigNumber)

讀取 Alice 的 USDT balance

需要使用 zksync.Wallet 物件的 getEthereumBalance 來取得 USDT balance

語法如下

const yourUSDTBalance = await yourZkSyncWallet.getEthereumBalance(token)

實作

  1. 刪除讀取原本讀取 ETH 的邏輯
  2. 移出 ethers.utils.formatEthter 語法
  3. 加入 const tokenSet = zksyncProvider.tokenSet
  4. 使用 getEthereumBalance 語法讀出 USDT balance
  5. 顯示 USDT balance

更新如下

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'USDT'
  const amountToDeposit = '6.0'
  const amountToTransfer = '2.0'
  const amountToWithdraw = '2.0'

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider)
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)
  
 // 2. Cut this line

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  // 3. Instantiate the `tokenSet` class
  const tokenSet = zkSyncProvider.tokenSet
  // 4. Retrieve Alice's initial USDT balance on Rinkeby
  const aliceInitialRinkebyBalance = await aliceZkSyncWallet.getEthereumBalance(token)
  // 5. Display Alice's balance
  console.log(`Alice's initial balance on Rinkeby is: ${tokenSet.formatToken(token, aliceInitialRinkebyBalance)}`) 

  console.log('Depositing')
  await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, ethers)
  await utils.displayZkSyncBalance(aliceZkSyncWallet, ethers)
  await utils.registerAccount(aliceZkSyncWallet)

  console.log('Transferring')
  const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee, token, zksync, ethers)

  console.log('Withdrawing')
  const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token, zksync, ethers)

})()

實作 approve ERC20 Token transfer

  1. 呼叫 aliceZkSyncWallet.approveERC20TokenDeposits(token)

更新如下

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'USDT'
  const amountToDeposit = '6.0'
  const amountToTransfer = '2.0'
  const amountToWithdraw = '2.0'

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider)
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  const tokenSet = zkSyncProvider.tokenSet
  const aliceInitialRinkebyBalance = await aliceZkSyncWallet.getEthereumBalance(token)
  console.log(`Alice's initial balance on Rinkeby is: ${tokenSet.formatToken(token, aliceInitialRinkebyBalance)}`)

  // Start here
  await aliceZkSyncWallet.approveERC20TokenDeposits(token)
  console.log('Depositing')
  await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, ethers)
  await utils.displayZkSyncBalance(aliceZkSyncWallet, ethers)
  await utils.registerAccount(aliceZkSyncWallet)

  console.log('Transferring')
  const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee, token, zksync, ethers)

  console.log('Withdrawing')
  const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
  await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token, zksync, ethers)
})()

修正所有 ethers 的功能改用 tokenSet

  1. utils.depositToZkSync 帶入以下參數
    • aliceZkSyncWallet
    • token
    • amountToDeposit
    • tokenSet
  2. utils.displayZkSyncBalance 帶入以下參數
    • aliceZkSyncWallet
    • tokenSet
  3. utils.getFee 帶入以下參數
    • 'Transfer' or 'Withdraw'
    • aliceRinkebyWallet.address
    • token
    • zkSyncProvicer
    • tokenSet
  4. utils.transfer 帶入以下參數
    • aliceZkSyncWallet
    • process.env.BOB_ADDRESS
    • amountToTransfer
    • transferFee
    • token
    • zksync
    • tokenSet
  5. utils.withdrawToEthereum 帶入以下參數
    • aliceZkSyncWallet
    • amountToWithdraw
    • withdrawalFee
    • token
    • zksync
    • tokenSet

更新如下

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'USDT'
  const amountToDeposit = '6.0'
  const amountToTransfer = '2.0'
  const amountToWithdraw = '2.0'

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider)
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  const tokenSet = zkSyncProvider.tokenSet
  const aliceInitialRinkebyBalance = await aliceZkSyncWallet.getEthereumBalance(token)
  console.log(`Alice's initial balance on Rinkeby is: ${tokenSet.formatToken(token, aliceInitialRinkebyBalance)}`)

  await aliceZkSyncWallet.approveERC20TokenDeposits(token)

  console.log('Depositing')
  await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, tokenSet) // Update this line
  await utils.displayZkSyncBalance(aliceZkSyncWallet, tokenSet) // Update this line
  await utils.registerAccount(aliceZkSyncWallet)

  console.log('Transferring')
  const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, tokenSet) // Update this line
  await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee, token, zksync, tokenSet) // Update this line

  console.log('Withdrawing')
  const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token, zkSyncProvider, tokenSet) // Update this line
  await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token, zksync, tokenSet) // Update this line
})()

更新 depositToZkSync 功能

  1. 修改 depositToSync 改用 tokenSet 來轉換 amountToDeposit 到 BigNumber
  2. zkSyncWallet.depositToSyncFromEthereum 只需要一個參數 物件型態
    需要設定 amount 屬行值為 tokenSet.parseToken(token, amountToDeposit)

更新如下

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    // eslint-disable-next-line new-cap
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if (!await wallet.isSigningKeySet()) {
    if (await wallet.getAccountId() === undefined) {
      throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
  console.log(`Account ${wallet.address()} registered`)
}

//1. On the next line, replace the last parameter (`ethers`) with `tokenSet`
async function depositToZkSync (zkSyncWallet, token, amountToDeposit, tokenSet ) {
  const deposit = await zkSyncWallet.depositToSyncFromEthereum({
    depositTo: zkSyncWallet.address(),
    token: token,
    amount: tokenSet.parseToken(token, amountToDeposit) //2. Call the `tokenSet.parseToken` function
  })
  try {
    await deposit.awaitReceipt()
  } catch (error) {
    console.log('Error while awaiting confirmation from the zkSync operators.')
    console.log(error)
  }
}

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, ethers) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(
    ethers.utils.parseEther(amountToTransfer))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(
    ethers.utils.parseEther(transferFee))

  const transfer = await from.syncTransfer({
    to: toAddress,
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  const transferReceipt = await transfer.awaitReceipt()
  console.log('Got transfer receipt.')
  console.log(transferReceipt)
}

async function getFee(transactionType, address, token, zkSyncProvider, ethers) {
  const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
  return ethers.utils.formatEther(feeInWei.totalFee.toString())
}

async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, ethers) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToWithdraw))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(withdrawalFee))
  const withdraw = await wallet.withdrawFromSyncToEthereum({
    ethAddress: wallet.address(),
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

async function displayZkSyncBalance (wallet, ethers) {
  const state = await wallet.getAccountState()

  if (state.committed.balances.ETH) {
    console.log(`Commited ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.committed.balances.ETH)}`)
  } else {
    console.log(`Commited ETH balance for ${wallet.address()}: 0`)
  }

  if (state.verified.balances.ETH) {
    console.log(`Verified ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.verified.balances.ETH)}`)
  } else {
    console.log(`Verified ETH balance for ${wallet.address()}: 0`)
  }
}

module.exports = {
  getZkSyncProvider,
  getEthereumProvider,
  depositToZkSync,
  registerAccount,
  displayZkSyncBalance,
  transfer,
  withdrawToEthereum,
  getFee,
  initAccount
}

更新 displayZkSyncBalance 功能

透過 wallet.getAccountState 可以讀取到 state 物件

語法如下

const state = await wallet.getAccountState()

假設把 state 列印出來會發現結果如下

{ address: '0xc26f2adeeebbad73f25329ffa12cd3889429b5b6',
  committed:
   { balances: { ETH: '99891300000000000', USDT: '241896200' },
     nonce: 5,
     pubKeyHash: 'sync:de9de11bdad08aa1cdc2beb5b2b7c7f29c10f079' },
  depositing: { balances: {} },
  id: 83,
  verified:
   { balances: { ETH: '99891300000000000', USDT: '235896200' },
     nonce: 5,
     pubKeyHash: 'sync:de9de11bdad08aa1cdc2beb5b2b7c7f29c10f079' }
}

實作 displayZkSyncBalance

  1. 修改 ethers 參數為 tokenSet
  2. 宣告 const 變數 committedBalances = state.committed.balances
  3. 宣告 const 變數 verifiedBalances = state.verified.balances
  4. 使用 for .. in 迴圈列印出 committedBalances 內所有值語法如下
for (const property in committedBalances) {
  console.log(`Committed ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, committedBalances[property])}`)
}
  1. 使用 for .. in 迴圈列印出 verifiedBalances 內所有值語法如下
for (const property in verifiedBalances) {
  console.log(`Committed ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, verifiedBalances[property])}`)
}

更新如下

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    // eslint-disable-next-line new-cap
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if (!await wallet.isSigningKeySet()) {
    if (await wallet.getAccountId() === undefined) {
      throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
  console.log(`Account ${wallet.address()} registered`)
}

async function depositToZkSync (zkSyncWallet, token, amountToDeposit, tokenSet) {
  const deposit = await zkSyncWallet.depositToSyncFromEthereum({
    depositTo: zkSyncWallet.address(),
    token: token,
    amount: tokenSet.parseToken(token, amountToDeposit)
  })
  try {
    await deposit.awaitReceipt()
  } catch (error) {
    console.log('Error while awaiting confirmation from the zkSync operators.')
    console.log(error)
  }
}

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, ethers) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(
    ethers.utils.parseEther(amountToTransfer))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(
    ethers.utils.parseEther(transferFee))

  const transfer = await from.syncTransfer({
    to: toAddress,
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  const transferReceipt = await transfer.awaitReceipt()
  console.log('Got transfer receipt.')
  console.log(transferReceipt)
}

async function getFee(transactionType, address, token, zkSyncProvider, ethers) {
  const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
  return ethers.utils.formatEther(feeInWei.totalFee.toString())
}

async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, ethers) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToWithdraw))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(withdrawalFee))
  const withdraw = await wallet.withdrawFromSyncToEthereum({
    ethAddress: wallet.address(),
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

// 1. On the next line, replace the last parameter (`ethers`) with `tokenSet`
async function displayZkSyncBalance (wallet, tokenSet) {
  const state = await wallet.getAccountState()
  
  // 2. Continue here
  const committedBalances = state.committed.balances
  const verifiedBalances = state.verified.balances
  for (const property in committedBalances) {
    console.log(`Commited ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, committedBalances[property])}`)
  }
  for (const property in verifiedBalances) {
    console.log(`Verified ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, verifiedBalances[property])}`)
  }
}

module.exports = {
  getZkSyncProvider,
  getEthereumProvider,
  depositToZkSync,
  registerAccount,
  displayZkSyncBalance,
  transfer,
  withdrawToEthereum,
  getFee,
  initAccount
}

更新 transfer function

  1. 修改 transfer 參數 ethers 為 tokenSet
  2. 改用 tokenSet.parseToken 來把 amountToTransfer 還有 fee 改成 BigNumber

更新如下

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    // eslint-disable-next-line new-cap
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if (!await wallet.isSigningKeySet()) {
    if (await wallet.getAccountId() === undefined) {
      throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
  console.log(`Account ${wallet.address()} registered`)
}

async function depositToZkSync (zkSyncWallet, token, amountToDeposit, tokenSet) {
  const deposit = await zkSyncWallet.depositToSyncFromEthereum({
    depositTo: zkSyncWallet.address(),
    token: token,
    amount: tokenSet.parseToken(token, amountToDeposit)
  })
  try {
    await deposit.awaitReceipt()
  } catch (error) {
    console.log('Error while awaiting confirmation from the zkSync operators.')
    console.log(error)
  }
}

// 1. On the next line, replace the last parameter (`ethers`) with `tokenSet`
async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, tokenSet) {
  // 2. Update the following two lines of code
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(
    tokenSet.parseToken(token, amountToTransfer))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(
    tokenSet.parseToken(token, transferFee))

  const transfer = await from.syncTransfer({
    to: toAddress,
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  const transferReceipt = await transfer.awaitReceipt()
  console.log('Got transfer receipt.')
  console.log(transferReceipt)
}

async function getFee(transactionType, address, token, zkSyncProvider, ethers) {
  const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
  return ethers.utils.formatEther(feeInWei.totalFee.toString())
}

async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, ethers) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToWithdraw))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(withdrawalFee))
  const withdraw = await wallet.withdrawFromSyncToEthereum({
    ethAddress: wallet.address(),
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

async function displayZkSyncBalance (wallet, tokenSet) {
  const state = await wallet.getAccountState()
  const commitedBbalances = state.committed.balances
  const verifiedBalances = state.verified.balances
  for (const property in commitedBbalances) {
    console.log(`Commited ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, commitedBbalances[property])}`)
  }
  for (const property in verifiedBalances) {
    console.log(`Verified ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, verifiedBalances[property])}`)
  }
}

module.exports = {
  getZkSyncProvider,
  getEthereumProvider,
  depositToZkSync,
  registerAccount,
  displayZkSyncBalance,
  transfer,
  withdrawToEthereum,
  getFee,
  initAccount
}

更新 withdrawToEthereum

  1. 修改 withdrawToEthereum 參數 ethers 為 tokenSet
  2. 改用 tokenSet.parseToken 來把 amountToWithdraw, withdrawalFee 轉換成 BigNumber

更新如下

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    // eslint-disable-next-line new-cap
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if (!await wallet.isSigningKeySet()) {
    if (await wallet.getAccountId() === undefined) {
      throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
  console.log(`Account ${wallet.address()} registered`)
}

async function depositToZkSync (zkSyncWallet, token, amountToDeposit, tokenSet) {
  const deposit = await zkSyncWallet.depositToSyncFromEthereum({
    depositTo: zkSyncWallet.address(),
    token: token,
    amount: tokenSet.parseToken(token, amountToDeposit)
  })
  try {
    await deposit.awaitReceipt()
  } catch (error) {
    console.log('Error while awaiting confirmation from the zkSync operators.')
    console.log(error)
  }
}

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, tokenSet) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(tokenSet.parseToken(token, amountToTransfer))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(tokenSet.parseToken(token, transferFee))

  const transfer = await from.syncTransfer({
    to: toAddress,
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  const transferReceipt = await transfer.awaitReceipt()
  console.log('Got transfer receipt.')
  console.log(transferReceipt)
}

async function getFee(transactionType, address, token, zkSyncProvider, ethers) {
  const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
  return ethers.utils.formatEther(feeInWei.totalFee.toString())
}

// 1. On the next line, replace the last parameter (`ethers`) with `tokenSet`
async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, tokenSet) {

  // 2. Update the following two lines of code

  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(tokenSet.parseToken(token, amountToWithdraw))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(tokenSet.parseToken(token, withdrawalFee))
  const withdraw = await wallet.withdrawFromSyncToEthereum({
    ethAddress: wallet.address(),
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

async function displayZkSyncBalance (wallet, tokenSet) {
  const state = await wallet.getAccountState()
  const commitedBbalances = state.committed.balances
  const verifiedBalances = state.verified.balances
  for (const property in commitedBbalances) {
    console.log(`Commited ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, commitedBbalances[property])}`)
  }
  for (const property in verifiedBalances) {
    console.log(`Verified ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, verifiedBalances[property])}`)
  }
}

module.exports = {
  getZkSyncProvider,
  getEthereumProvider,
  depositToZkSync,
  registerAccount,
  displayZkSyncBalance,
  transfer,
  withdrawToEthereum,
  getFee,
  initAccount
}

更新 getFee

  1. 修改 getFee 的參數 ethers 為 tokenSet
  2. 改用 tokenSet.formatToken 把 fee.totalFee 從 BigNumber 轉為可讀形式

更新如下

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    // eslint-disable-next-line new-cap
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if (!await wallet.isSigningKeySet()) {
    if (await wallet.getAccountId() === undefined) {
      throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
  console.log(`Account ${wallet.address()} registered`)
}

async function depositToZkSync (zkSyncWallet, token, amountToDeposit, tokenSet) {
  const deposit = await zkSyncWallet.depositToSyncFromEthereum({
    depositTo: zkSyncWallet.address(),
    token: token,
    amount: tokenSet.parseToken(token, amountToDeposit)
  })
  try {
    await deposit.awaitReceipt()
  } catch (error) {
    console.log('Error while awaiting confirmation from the zkSync operators.')
    console.log(error)
  }
}

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, tokenSet) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(tokenSet.parseToken(token, amountToTransfer))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(tokenSet.parseToken(token, transferFee))

  const transfer = await from.syncTransfer({
    to: toAddress,
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  const transferReceipt = await transfer.awaitReceipt()
  console.log('Got transfer receipt.')
  console.log(transferReceipt)
}

// 1. On the next line, replace the last parameter (`ethers`) with `tokenSet`
async function getFee(transactionType, address, token, zkSyncProvider, tokenSet) {
  const fee = await zkSyncProvider.getTransactionFee(transactionType, address, token)

  // 2. On the next line, use the `tokenSet` object instead of `ethers.utils.formatEther`
  return tokenSet.formatToken(token, fee.totalFee)
}

async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, tokenSet) {
  const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(tokenSet.parseToken(token, amountToWithdraw))
  const closestPackableFee = zksync.utils.closestPackableTransactionFee(tokenSet.parseToken(token, withdrawalFee))
  const withdraw = await wallet.withdrawFromSyncToEthereum({
    ethAddress: wallet.address(),
    token: token,
    amount: closestPackableAmount,
    fee: closestPackableFee
  })
  await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

async function displayZkSyncBalance (wallet, tokenSet) {
  const state = await wallet.getAccountState()
  const commitedBbalances = state.committed.balances
  const verifiedBalances = state.verified.balances
  for (const property in commitedBbalances) {
    console.log(`Commited ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, commitedBbalances[property])}`)
  }
  for (const property in verifiedBalances) {
    console.log(`Verified ${property} balance for ${wallet.address()}: ${tokenSet.formatToken(property, verifiedBalances[property])}`)
  }
}

module.exports = {
  getZkSyncProvider,
  getEthereumProvider,
  depositToZkSync,
  registerAccount,
  displayZkSyncBalance,
  transfer,
  withdrawToEthereum,
  getFee,
  initAccount
}


上一篇
從以太坊白皮書理解 web 3 概念 - Day21
下一篇
從以太坊白皮書理解 web 3 概念 - Day23
系列文
從以太坊白皮書理解 web 3 概念32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言